package com.zly;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.zly.entity.DynamicJsonNode;
import com.zly.entity.DynamicSegment;
import com.zly.entity.FunctionData;
import com.zly.entity.block.*;
import com.zly.entity.enums.BlockType;
import com.zly.entity.enums.Keyword;
import com.zly.entity.enums.SegmentType;
import com.zly.entity.parameter.DirectValueParameter;
import com.zly.entity.parameter.DynamicJsonParameter;
import com.zly.entity.parameter.FunctionReturnValueParameter;

import java.lang.reflect.Array;
import java.util.*;

public class DJEntityFactory {

    private static final Object[] EMPTY_ARGS = new Object[0];

    //构造
    private DJEntityFactory(){}

    //生产DynamicJson
    public static DynamicJsonNode produceDynamicJsonNode(JsonNode rootNode){

        DynamicJsonNode dynamicJsonNode = null;{

            //获取Segment
            DynamicSegment dynamicSegment = null;{
                //必须有segment键
                if(rootNode.has(Keyword.SEGMENT.getStrValue())){

                    dynamicSegment = produceSegment(rootNode.get(Keyword.SEGMENT.getStrValue()));

                }else{//若没有
                    throw new RuntimeException("DynamicJson中不存在"+Keyword.SEGMENT.getStrValue()+":"+rootNode.toPrettyString());
                }

            }

            //获取parameterArray
            DynamicJsonParameter[] parameterArray = null;{

                //允许parameterArray为空
                if(rootNode.has(Keyword.PLACEHOLDER_PARAMETERS.getStrValue())){

                    ArrayNode paramJsonArray = (ArrayNode) rootNode.get(Keyword.PLACEHOLDER_PARAMETERS.getStrValue());
                    parameterArray = new DynamicJsonParameter[paramJsonArray.size()];
                    for(int i = 0; i < paramJsonArray.size(); i++){
                        JsonNode parameterNode = paramJsonArray.get(i);
                        DynamicJsonParameter parameter = produceParameter(parameterNode);
                        parameterArray[i] = parameter;
                    }

                }

            }

            dynamicJsonNode = new DynamicJsonNode(dynamicSegment, parameterArray);

        }

        return dynamicJsonNode;

    }

    //根据函数返回值节点生产包含函数名和参数列表的FunctionData，为对外接口
    public static FunctionData produceFunctionData(JsonNode valueNode){

        String functionName = valueNode.get(Keyword.PARAMETER_VALUE_FUNCTION_NAME.getStrValue()).asText();
        Object[] args = null;{
            //若存在function_args
            if(valueNode.has(Keyword.PARAMETER_VALUE_FUNCTION_ARGS.getStrValue())){

                boolean allConvertArray = false;
                int[] needConvertIndexArray = null;
                //若存在arg_as_array，进行处理；若不存在arg_as_array，默认全部不需要转换为Java数组
                if(valueNode.has(Keyword.PARAMETER_VALUE_ARG_AS_ARRAY.getStrValue())){

                    JsonNode argAsArrayNode = valueNode.get(Keyword.PARAMETER_VALUE_ARG_AS_ARRAY.getStrValue());
                    if(argAsArrayNode.isBoolean()){//若arg_as_array属性为Json布尔类型
                        allConvertArray = argAsArrayNode.asBoolean();
                    }else if(argAsArrayNode.isArray()){//若arg_as_array属性为Json数组

                        ArrayNode indexArrayNode = (ArrayNode) argAsArrayNode;
                        needConvertIndexArray = new int[indexArrayNode.size()];
                        for(int i = 0; i < indexArrayNode.size(); i++){
                            JsonNode indexNode = indexArrayNode.get(i);
                            if(indexNode.isInt()){
                                needConvertIndexArray[i] = indexArrayNode.get(i).asInt();
                            }else{
                                throw new RuntimeException("arg_as_array数组仅允许整型元素:" + argAsArrayNode.toPrettyString());
                            }
                        }

                    }else{
                        throw new RuntimeException("arg_as_array属性仅允许true,false或Json数组:" + argAsArrayNode.toPrettyString());
                    }

                }

                JsonNode temp = valueNode.get(Keyword.PARAMETER_VALUE_FUNCTION_ARGS.getStrValue());
                //若function_args属性为Json数组
                if(temp.isArray()){

                    ArrayNode argsArrayNode = (ArrayNode) temp;
                    if(!argsArrayNode.isEmpty()){//若function_args这个Json数组不为空

                        args = new Object[argsArrayNode.size()];
                        for(int i = 0; i < argsArrayNode.size(); i++){

                            JsonNode node = argsArrayNode.get(i);

                            if(node.isArray() && (allConvertArray || (needConvertIndexArray != null && contain(needConvertIndexArray, i)))){
                                args[i] = convertToJavaArray((ArrayNode) node);
                            }else{
                                args[i] = DJUtils.convert(argsArrayNode.get(i));
                            }

                        }

                    }

                }else{//若function_args属性不为Json数组，将其值作为唯一参数

                    args = new Object[1];
                    args[0] = DJUtils.convert(temp);

                    //若function_args属性不为Json数组，仅当arg_as_array属性为true时将唯一参数转换为长度为1的Java数组
                    if(allConvertArray){
                        var value = args[0];
                        Class<?> clazz = value == null ? Object.class : value.getClass();
                        args[0] = Array.newInstance(clazz, 1);
                        Array.set(args[0], 0, value);
                    }

                }

            }else{//若不存在function_args，为使后面的处理方便而正确，设置args为长度为0的空数组，而不为null
                args = EMPTY_ARGS;
            }
        }

        return new FunctionData(functionName, args);

    }

    //生产DynamicJsonParameter
    private static DynamicJsonParameter produceParameter(JsonNode parameterNode){

        DynamicJsonParameter dynamicJsonParameter = null;{
            //检查参数Json的必要键:value。允许省略name
            if(!parameterNode.has(Keyword.PARAMETER_VALUE.getStrValue())){
                throw new RuntimeException("Parameter缺乏必要的键:"+Keyword.PARAMETER_VALUE.getStrValue()+":"+parameterNode.toPrettyString());
            }

            //获取parameterName，允许不存在
            String parameterName = null;{
                //若存在name
                if(parameterNode.has(Keyword.PARAMETER_NAME.getStrValue())){

                    parameterName = parameterNode.get(Keyword.PARAMETER_NAME.getStrValue()).asText();
                    //参数名称不可以数字开头，因为省略参数名称的情况下参数会以下标作为键值对中的键名
                    if(Character.isDigit(parameterName.charAt(0))){
                        throw new RuntimeException("Parameter的名称不允许以数字开头:" + parameterNode.toPrettyString());
                    }
                }
            }

            //检查参数是否是函数返回值
            JsonNode valueNode = null;
            boolean isFunctionReturnValue;
            {
                //若显示指定了是否为函数返回值
                if(parameterNode.has(Keyword.PARAMETER_IS_FUNCTION_RESULT.getStrValue())){
                    isFunctionReturnValue = parameterNode.get(Keyword.PARAMETER_IS_FUNCTION_RESULT.getStrValue()).asBoolean();
                }else{//若未显示指定，检查value的值是否为Json对象，且该对象有function_name键

                    valueNode = parameterNode.get(Keyword.PARAMETER_VALUE.getStrValue());
                    if(valueNode.isObject() && valueNode.has(Keyword.PARAMETER_VALUE_FUNCTION_NAME.getStrValue())){
                        isFunctionReturnValue = true;
                    }else{
                        isFunctionReturnValue = false;
                    }

                }
            }

            //实例化
            if(valueNode == null){//本方法前面的代码可能已经获取了valueNode
                valueNode = parameterNode.get(Keyword.PARAMETER_VALUE.getStrValue());
            }
            if(isFunctionReturnValue){//若参数的值为函数返回值，获取值并实例化
                //若为函数返回值，获取相关值
                FunctionData functionData = produceFunctionData(valueNode);
                dynamicJsonParameter = new FunctionReturnValueParameter(parameterName, functionData.getFunctionName(), functionData.getFunctionArgs());

            }else{//若参数的值为直接值，获取值并实例化

                dynamicJsonParameter = new DirectValueParameter(parameterName, valueNode);

            }

        }

        return dynamicJsonParameter;

    }

    //生产DynamicSegment
    private static DynamicSegment produceSegment(JsonNode segmentNode){

        //获取DynamicSegment
        DynamicSegment dynamicSegment = null;{

            //获取segmentType
            SegmentType segmentType = null;{
                //先检查Json是否有完整的键值对形式记录的SegmentType信息
                if(segmentNode.has(Keyword.SEGMENT_TYPE.getStrValue())){

                    segmentType = SegmentType.from(segmentNode.get(Keyword.SEGMENT_TYPE.getStrValue()).asText());

                }else{//若没有

                    //检查segment_content是否存在
                    if(segmentNode.has(Keyword.SEGMENT_CONTENT.getStrValue())){//若存在，视作省略形式的SingleBlockSegment

                        segmentType = SegmentType.SINGLE_BLOCK_SEGMENT;

                    }else{//若不存在，视作隐式的SingleBlockSegment

                        segmentType = SegmentType.IMPLICIT_SINGLE_BLOCK_SEGMENT;

                    }

                }
            }

            //获取并设置blockArray
            DynamicBlock[] blockArray = null;{
                //若为隐式SingleBlockSegment，该Segment本身既为Block
                if(segmentType == SegmentType.IMPLICIT_SINGLE_BLOCK_SEGMENT){

                    blockArray = new DynamicBlock[1];
                    blockArray[0] = produceBlock(segmentNode);

                }else if(segmentType == SegmentType.SINGLE_BLOCK_SEGMENT){//若为显式，或在Json中省略segment_type的SingleBlockSegment

                    blockArray = new DynamicBlock[1];
                    blockArray[0] = produceBlock(segmentNode.get(Keyword.SEGMENT_CONTENT.getStrValue()));

                }else{//若为ChooseSegment

                    blockArray = produceBlockArray(segmentNode.get(Keyword.SEGMENT_CONTENT.getStrValue()));

                }

            }

            //实例化
            dynamicSegment = new DynamicSegment(segmentType,blockArray);

        }

        return dynamicSegment;

    }

    //生产DynamicBlockArray
    private static DynamicBlock[] produceBlockArray(JsonNode segmentContentNode){

        ArrayNode blockJsonArray = (ArrayNode) segmentContentNode;
        DynamicBlock[] dynamicBlocks = new DynamicBlock[blockJsonArray.size()];
        for (int i = 0; i < blockJsonArray.size(); i++) {
            dynamicBlocks[i] = produceBlock(blockJsonArray.get(i));
        }

        return dynamicBlocks;

    }

    //生产DynamicBlock
    private static DynamicBlock produceBlock(JsonNode blockNode){

        //获取DynamicBlock
        DynamicBlock block = null;{

            //获取BlockType
            boolean isAbbrWhenBlock = false;
            BlockType blockType = null;
            {

                //先检查Json是否有完整的键值对形式记录的BlockType信息
                if(blockNode.has(Keyword.BLOCK_TYPE.getStrValue())){

                    blockType = BlockType.from(blockNode.get(Keyword.BLOCK_TYPE.getStrValue()).asText());

                }else{//按照简写或省略形式获取BlockType

                    //检查当前Block的简写BlockType，不允许有多个
                    List<String> foundAbbrs = new ArrayList<>();{

                        for(BlockType hasAbbrBlockType : BlockType.hasAbbrKeywordValues()){
                            String abbrKeywordString = Objects.requireNonNull(hasAbbrBlockType.toKeyword()).getStrValue();
                            if(blockNode.has(abbrKeywordString)){
                                foundAbbrs.add(abbrKeywordString);
                            }
                        }

                    }
                    if(foundAbbrs.isEmpty()){//若Block内无BlockType简写，视为隐式NormalBlock，既IMPLICIT_NORMAL_BLOCK

                        blockType = BlockType.IMPLICIT_NORMAL_BLOCK;

                    }else if(foundAbbrs.size() == 1){//若找到Block内有1个BlockType简写，根据简写读取

                        String abbrString = foundAbbrs.get(0);
                        Keyword abbrKeyword = Keyword.from(abbrString);
                        blockType = BlockType.from(abbrKeyword);
                        if(blockType == BlockType.WHEN_BLOCK){
                            isAbbrWhenBlock = true;
                        }

                    }else{//若Block内存在多个BlockType简写，则该Block的Json不符合规范
                        throw new RuntimeException("Block包含多个BlockType的简写:" + blockNode.toPrettyString());
                    }

                }

            }

            //获取BlockContent
            JsonNode contentNode = null;{
                //若为隐式NormalBlock，该Block本身即是BlockContent
                if(blockType == BlockType.IMPLICIT_NORMAL_BLOCK){
                    contentNode = blockNode;
                }else{//若不是隐式NormalBlock，Json中必须有block_content可读取
                    if(blockNode.has(Keyword.BLOCK_CONTENT.getStrValue())){
                        contentNode = blockNode.get(Keyword.BLOCK_CONTENT.getStrValue());
                    }else{
                        throw new RuntimeException("非隐式的NormalBlock中不存在" + Keyword.BLOCK_CONTENT.getStrValue() + ":" + blockNode.toPrettyString());
                    }
                }
            }

            //根据类型实例化，如需要同时获取额外信息
            if(blockType == BlockType.WHEN_BLOCK){

                //获取WhenBlock的额外信息condExpr
                String condExpr = null;{
                    if(isAbbrWhenBlock){//若为简写的WhenBlock
                        condExpr = blockNode.get(Keyword.BLOCK_TYPE_ABBR_WHEN.getStrValue()).asText();
                    }else{//若不为简写的WhenBlock，Json中必须有conditional_expression可读取
                        if(blockNode.has(Keyword.WHEN_BLOCK_COND_EXPR.getStrValue())){
                            condExpr = blockNode.get(Keyword.WHEN_BLOCK_COND_EXPR.getStrValue()).asText();
                        }else{
                            throw new RuntimeException("WhenBlock中不存在" + Keyword.WHEN_BLOCK_COND_EXPR.getStrValue() + ":" + blockNode.toPrettyString());
                        }
                    }
                }
                block = new WhenBlock(blockType, contentNode, condExpr);

            }else if(blockType == BlockType.OTHERWISE_BLOCK){//若为OTHERWISE_BLOCK
                block = new OtherwiseBlock(blockType, contentNode);
            }else{//若为EXPLICIT_NORMAL_BLOCK或IMPLICIT_NORMAL_BLOCK
                block = new NormalBlock(blockType, contentNode);
            }

        }

        return block;

    }

    //尝试将Json数组转换为Java数组
    private static Object convertToJavaArray(ArrayNode arrayNode){

        if(arrayNode.isEmpty()){
            return new Object[0];
        }

        JsonNodeType firstType = arrayNode.get(0).getNodeType();
        for(int i = 1; i < arrayNode.size(); i++){
            if(arrayNode.get(i).getNodeType() != firstType){
                throw new RuntimeException("将Json数组转换为Java数组失败，Json数组的元素类型不一致:" + arrayNode.toPrettyString());
            }
        }

        Object temp = DJUtils.convert(arrayNode.get(0));
        if(temp == null){

            Object[] nullArray = new Object[arrayNode.size()];
            for (int i = 0; i < arrayNode.size(); i++) {
                nullArray[i] = null;
            }
            return nullArray;

        }else{

            Class<?> clazz = temp.getClass();
            Object resultArray = Array.newInstance(clazz, arrayNode.size());
            for(int i = 0; i < arrayNode.size(); i++){
                Array.set(resultArray, i, DJUtils.convert(arrayNode.get(i)));
            }

            return resultArray;

        }


    }

    //检查数组中是否有指定元素
    private static boolean contain(int[] array,int elem){
        for (int n : array) {
            if (n == elem) {
                return true;
            }
        }
        return false;
    }

}
